package com.lplb.common.coreconfig.config;


import com.lplb.common.core.result.R;
import com.lplb.common.core.result.RType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;


/**
 * 异常处理类/ 全局异常 /自定义异常
 *
 * @author 王松
 * @mail 1720696548@qq.com
 * @date 2020/7/13 0013 15:52
 */
@SuppressWarnings("all")
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @Autowired
    public HttpServletRequest request;

    /**
     * 运行时异常log4j日志信息转换中文提示，key=为异常类，value=提示信息，可任意扩展
     */
    private static Map<String, String> errorMsgMap = null;

    /**
     * 程序错误
     */
    public GlobalExceptionHandler() {
        errorMsgMap = new HashMap<>();
        errorMsgMap.put("NullPointerException", "空指针异常");
        errorMsgMap.put("NumberFormatException", "字符串转换为数字异常,比如int i= Integer.parseInt(“ab3”)");
        errorMsgMap.put("ArrayIndexOutOfBoundsException", "数组越界");
        errorMsgMap.put("StringIndexOutOfBoundsException", "字符串越界");
        errorMsgMap.put("ClassCastException", "类型转换错误。比如 Object obj=new Object(); String s=(String)obj");
        errorMsgMap.put("UnsupportedOperationException", "该操作不被支持");
        errorMsgMap.put("ArithmeticException", "算术错误，典型的就是0作为除数的时候");
        errorMsgMap.put("IllegalArgumentException", "非法参数，在把字符串转换成数字的时候经常出现的一个异常");
        errorMsgMap.put("UnsupportedEncodingException", "编码编译出错");
        errorMsgMap.put("StackOverflowError", "由于深度递归，抛出此错误以指示应用程序的堆栈已耗尽");
        errorMsgMap.put("UndeclaredThrowableException", "被限流/线程卡死");
    }

    /**
     * 全局异常|受检查异常（所有未知异常） --> Exception 。可以理解为错误，必须要开发者解决以后才能编译通过，这里JSR 303 为受检查异常
     */
    @ExceptionHandler(Exception.class)
    public R<Void> exceptionHandler(Exception e) {
        // 错误类名
        String exClass = e.getClass().getName();
        String exClassName = exClass.substring(exClass.lastIndexOf(".") + 1, exClass.length());
        // 获取错误信息
        String errorMsg = "受检查异常: ";
        if (errorMsgMap.containsKey(exClassName)) {
            errorMsg = errorMsgMap.get(exClassName) + " : " + e;
        } else {
            errorMsg = errorMsgMap.get(exClassName);
        }
        errorMsg += "\r\n" + ExceptionUtils.getStackTrace(e);
        // 打印错误信息
        this.outLog(RType.SYS_ERROR.getCode(), RType.SYS_ERROR.getMsg(), errorMsg);
        return new R<>(RType.SYS_ERROR.getCode(), RType.SYS_ERROR.getMsg(), null, errorMsg);
    }


    /**
     * 全局异常|运行时异常 --> RuntimeException extends Exception： 运行时异常,又称不受检查异常，不受检查！
     *
     * @param e
     * @return R<java.lang.Void>
     * @date 2020/7/13 0013 15:35
     */
    @ExceptionHandler(RuntimeException.class)
    public R<Void> exceptionHandler(RuntimeException e) {
        // 错误类名
        String exClass = e.getClass().getName();
        String exClassName = exClass.substring(exClass.lastIndexOf(".") + 1, exClass.length());
        // 获取错误信息
        String errorMsg = "运行时异常: ";
        if (errorMsgMap.containsKey(exClassName)) {
            errorMsg = errorMsgMap.get(exClassName) + " : " + e;
        } else {
            errorMsg = errorMsgMap.get(exClassName);
        }
        errorMsg += "\r\n" + ExceptionUtils.getStackTrace(e);
        // 打印错误信息
        this.outLog(RType.SYS_ERROR.getCode(), RType.SYS_ERROR.getMsg(), errorMsg);
        return new R<>(RType.SYS_ERROR.getCode(), RType.SYS_ERROR.getMsg(), null, errorMsg);
    }


    /**
     * 请求参数错误
     * MethodArgumentNotValidException:jsr 303 验证不通过,参数不规范
     * MissingServletRequestParameterException:参数错误.未传递 Parameter 参数
     * MethodArgumentTypeMismatchException:方法参数类型不匹配
     *
     * @param e
     * @return R<java.lang.Void>
     * @date 2020/7/13 0013 15:35
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, MissingServletRequestParameterException.class, MethodArgumentTypeMismatchException.class})
    public R<Void> paramExceptionHandler(Exception e) {
        // 打印错误信息
        this.outLog(RType.SYS_ERROR_PARAM.getCode(), RType.SYS_ERROR_PARAM.getMsg(), e.toString());
        return new R<>(RType.SYS_ERROR_PARAM.getCode(), RType.SYS_ERROR_PARAM.getMsg(), null, e.toString());
    }


    /**
     * 自定义异常类捕获（返回用户/前端友好提示）,任意地方使用：throw new ErrorException(1000000,"自定义异常测试") ,
     *
     * @param e
     * @return R<java.lang.Void>
     * @date 2020/7/13 0013 15:30
     */
    @ExceptionHandler(ErrorException.class)
    public R<Void> exceptionHandler(ErrorException e) {
        //ErrorException error = (ErrorException) e;
        this.outLog(e.getCode(), e.getMsg(), e.getErrorMsg());
        return new R<>(e.getCode(), e.getMsg(), null,  e.getErrorMsg());
    }


    /**
     * 错误日志打印
     *
     * @param code     错误code
     * @param msg      错误信息
     * @param errorMsg 系统抛出的栈信息
     * @return void
     * @date 2020/7/13 0013 15:27
     */
    private void outLog(Integer code, String msg, String errorMsg) {
        log.error("### [全局异常] --> 请求URL:{} --> [code:{},msg:{},errorMsg:{}]", request.getRequestURL(), code, msg, errorMsg);
    }
}

